#include "EventDns.h"
#include "Service.h"
#include <string>
#include <event.h>
using namespace std;
using namespace std::tr1;
unordered_map<std::string, EventDnsCache*> EventDns::m_mapCache;
unordered_map<std::string, EventDns*> EventDns::m_mapProcessing;
struct request_record {
	char host[256];
};

struct response_record {
	int type;
	char ip[64];
	char cname[256];
	char host[256];
	int ttl;
};

struct EventDnsCache {
	string ip;
	__int64 timeoutTime;
};

static __int64 EventDnsGetTimestamp() {
	return GetTickCount();
	/*
	timeval val = {};
	evutil_gettimeofday(&val, NULL);
	return (__int64)(val.tv_sec * 1000) + val.tv_usec / 1000;
	*/
}

EventDns::EventDns()
{
	m_tmr = NULL;
	m_event = NULL;
	m_callbackInTime = false;
	m_isSuccess = false;
	this->m_fd = 0;
	this->m_event = NULL;
}

EventDns::~EventDns()
{
	if (this->m_event) {
		event_del(this->m_event);
		event_free(this->m_event);
		this->m_event = NULL;

		unordered_map<std::string, EventDns*>::iterator iter = m_mapProcessing.find(m_host);
		if (iter != m_mapProcessing.end()) {
			m_mapProcessing.erase(iter);
		}
	}

	if (this->m_fd) {
		evutil_closesocket(this->m_fd);
		this->m_fd = 0;
	}

	if (this->m_tmr) {
		evtimer_del(this->m_tmr);
		event_free(this->m_tmr);
		this->m_tmr = NULL;
	}
	
}

static unsigned char *installDomainName(unsigned char *p, const char *domain_name)
{
	// .lemuria.cis.vtc.edu\0
	*p++ = '.';

	strcpy((char *)p, domain_name);
	p--;

	while (*p != '\0') {
		if (*p == '.') {
			unsigned char *end = p + 1;
			while (*end != '.' && *end != '\0') end++;
			*p = end - p - 1;
		}
		p++;
	}
	return p + 1;
}

static string getRequestBuffer(const char* host)
{
	static WORD id = 0;
	id ++;
	string ret;
	unsigned char  send_buf[4096] = {};

	unsigned char *p = NULL;

	p = send_buf;
	p[0] = id & 0xff;
	p[1] = id >> 8;	
	p[2] = 0x1; //QR = 0, Opcode = 0, AA = 0, TC = 0, RD = 1.
	p[5] = 0x1; //QDCOUNT = 1
	p += 12;

	p = installDomainName(p, host);
	p[1] = 0x1; // qtype=1
	p[3] = 1;
	p += 4;
	int bufferLen = p - send_buf;
	ret.append((char*)send_buf, bufferLen);
	return ret;
}

EventDns * EventDns::query(Service * service, const std::string & hostName,  EventDnsHandler* handler)
{	
	int len = sizeof(EventDnsCache);
	int result = 0;
	EventDns* ret = new EventDns();
	ret->m_host = hostName;
	ret->m_handler = handler;

	
	if (INADDR_NONE != inet_addr(hostName.c_str())) {
		ret->m_ip = hostName;
		ret->m_tmr = evtimer_new(service->getBase(), EventDns::onTimer, ret);
		ret->m_isSuccess = true;
		timeval val = {};
		event_add(ret->m_tmr, &val);
		return ret;
	}
	unordered_map<std::string, EventDnsCache*>::iterator iter = EventDns::m_mapCache.find(hostName);
	if (iter != m_mapCache.end()) {
		EventDnsCache* cache = iter->second;

		__int64 currentTime = EventDnsGetTimestamp();

		if (currentTime > cache->timeoutTime) {
			delete iter->second;
			m_mapCache.erase(hostName);
		} else {
			ret->m_ip = iter->second->ip;
			ret->m_tmr = evtimer_new(service->getBase(), EventDns::onTimer, ret);
			ret->m_isSuccess = true;
			timeval val = {};
			event_add(ret->m_tmr, &val);
			return ret;
		}		
	}

	unordered_map<std::string, EventDns*>::iterator iterFollower = m_mapProcessing.find(hostName);
	if(iterFollower != m_mapProcessing.end()){
		iterFollower->second->m_follower.push_back(ret);
		return ret;
	}
	
	sockaddr_in addr = {};
	addr.sin_addr.s_addr = inet_addr("114.114.114.114");
	addr.sin_port = htons(53);
	addr.sin_family = AF_INET;

	ret->m_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	evutil_make_socket_nonblocking(ret->m_fd);
	timeval val = { 30, 0 };
	ret->m_event = event_new(service->getBase(), ret->m_fd, EV_READ, &EventDns::onRecv, ret);
	event_add(ret->m_event, &val);
	
	
	string requestBuffer = getRequestBuffer(hostName.c_str());
	
	result = connect(ret->m_fd, (sockaddr*)&addr, sizeof(sockaddr_in));

	if (result != 0) {
		ret->m_tmr = evtimer_new(0, EventDns::onTimer, ret);
		ret->m_isSuccess = true;
		timeval val = {};
		event_add(ret->m_tmr, &val);
		return ret;
	}
	m_mapProcessing[hostName] = ret;
	result = send(ret->m_fd, requestBuffer.c_str(), requestBuffer.size(), 0);
	return ret;
}

static unsigned char* decodeDnsEncodedString(unsigned char* msg_begin, unsigned char* p, char* buf) {
	while (*p) {
		int len = *p;

		if (len == 0xc0) {
			int offset = (*p & 0x3f) * 255 + (*(p + 1));
			decodeDnsEncodedString(msg_begin, msg_begin + offset, buf);
			p += 2;
			return p;
		}
		else {
			p++;
			memcpy(buf + strlen(buf), p, len);
			strcat(buf, ".");
			p += len;
		}
	}
	buf[strlen(buf) - 1] = 0;
	p++;
	return p;
}

static unsigned char* decodeDnsResopnseItem(unsigned char* msg_begin, unsigned char* p, struct response_record* resp) {
	int i = 0;
	p = decodeDnsEncodedString(msg_begin, p, resp->host);

	int type = (*p) * 255 + (*(p + 1));
	p += 2;
	int qclass = (*p) * 255 + (*(p + 1));
	p += 2;
	int ttl = ntohl(*(DWORD*)p);
	p += 4;
	int rdlen = (*p) * 255 + (*(p + 1));
	p += 2;

	resp->type = type;

	if (type == 0x1) {
		for (i = 0; i < 4; i++) {
			sprintf_s(resp->ip, sizeof(resp->ip), "%d.%d.%d.%d", *p, *(p + 1), *(p + 2), *(p + 3));
			resp->ttl = ttl;
		}
	}
	else if (type == 0x5) {
		decodeDnsEncodedString(msg_begin, p, resp->cname);
		printf("cname:%s\n", resp->cname);
	}

	p += rdlen;
	return p;

}

static unsigned char* decodeDnsRequestItems(unsigned char* msg_begin, unsigned char* p, struct request_record* rec) {
	p = decodeDnsEncodedString(msg_begin, p, rec->host);
	printf("host = %s\n", rec->host);
	p += 4;
	return p;
}

void EventDns::decodeResponse(const char* resp, int len) {	
	int i = 0;
	struct request_record* reqs = NULL;
	struct response_record* resps = NULL;
	struct response_record* ip_item = NULL;
	unsigned char* p = (unsigned char*)resp;
	int id = (*p) * 255 + (*(p + 1));
	int qr = (*(p + 2)) >> 7;
	int rcode = (*(p + 3) & 0xf);
	int dcount = (*(p + 4)) * 255 + (*(p + 5));
	int answer_count = (*(p + 6)) * 255 + (*(p + 7));



	if (dcount) {
		reqs = (struct request_record*)calloc(1, sizeof(struct request_record) * dcount);
	}

	if (answer_count) {
		resps = (struct response_record*)calloc(1, sizeof(struct response_record) * answer_count);
	}

	// skip header
	p += 12;

	// skip request
	for (i = 0; i< dcount; i++) {
		p = decodeDnsRequestItems((unsigned char*)resp, p, reqs + i);
	}

	for (i = 0; i < answer_count; i++) {
		p = decodeDnsResopnseItem((unsigned char*)resp, p, resps + i);
	}

	
	if (reqs) {
		free(reqs);
	}

	for(int i = 0; i < answer_count; i++) {
		if(strlen(resps[i].ip)) {
			ip_item = resps + i;
			break;
		}
	}
	
	if (ip_item) {		
		this->m_isSuccess = true;
		this->m_ip = ip_item->ip;
		this->activeCallback();

		unordered_map<std::string, EventDnsCache*>::iterator iter = m_mapCache.find(m_host);
		if(iter != m_mapCache.end()){
			delete iter->second;
		}
		EventDnsCache* cache = new EventDnsCache;
		cache->ip = ip_item->ip;
		cache->timeoutTime = EventDnsGetTimestamp() + ip_item->ttl * 1000 ;
		m_mapCache[this->m_host] = cache;		
	}
	else {
		this->m_isSuccess = false;
		this->activeCallback();
	}

	if(resps) {
		free(resps);
	}
}

void EventDns::activeCallback()
{
	if (m_handler) {
		m_handler->onDnsResolve(this, this->m_isSuccess, this->m_ip);
	}

	for (int i = 0; i < m_follower.size(); i++) {
		EventDns* dns = m_follower[i];
		if (dns->m_handler) {
			dns->m_handler->onDnsResolve(dns, this->m_isSuccess, this->m_ip);
		}
		delete dns;
	}
}

void EventDns::onRecv(intptr_t fd, short ev, void * args)
{
	EventDns* dns = (EventDns*)args;
	char buf[4096] = {};
	int ret = recv(fd, buf, sizeof(buf), 0);

	if (ret <= 0) {
		dns->m_isSuccess = false;
		dns->activeCallback();
		delete dns;
		return;
	}

	dns->decodeResponse(buf, sizeof(buf));
	delete dns;
}

void EventDns::onTimer(intptr_t fd, short ev, void * args)
{
	EventDns* dns = (EventDns*)args;	
	dns->activeCallback();
	delete dns;
}

void EventDns::clear()
{
	for (unordered_map<std::string, EventDnsCache*>::iterator iter = m_mapCache.begin(); iter != m_mapCache.end(); iter++) {
		delete iter->second;
	}
	m_mapCache.clear();
}
